/**
 * com.chaeron.GPS - An API for access to GPS devices.
 *
 *
 * Copyright (c) 2001, 2002 Chaeron Corporation,
 * All rights reserved.
 * 
 * Chaeron grants you a non-exclusive license to use, modify and re-distribute
 * this program under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version, provided that this copyright notice and 
 * license appear on all copies of the software.
 * 
 * Software is provided "AS IS," without a warranty of any kind. ALL EXPRESS OR
 * IMPLIED REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE
 * HEREBY EXCLUDED. THE ENTIRE RISK ARISING OUT OF USING THE SOFTWARE IS ASSUMED
 * BY THE LICENSEE. See the GNU General Public License for more details.
 *
 * CHAERON AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
 * LICENSEE OR ANY THIRD PARTY AS A RESULT OF USING OR DISTRIBUTING SOFTWARE.
 * IN NO EVENT WILL CHAERON OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE,
 * PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL
 * OR PUNITIVE DAMAGES, HOWEVER CAUSED AN REGARDLESS OF THE THEORY OF LIABILITY,
 * ARISING OUT OF THE USE OF OR INABILITY TO USE SOFTWARE, EVEN IF CHAERON HAS
 * BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

 * @version     1.02
 * @author  	Andrzej Jan Taramina, Chaeron Corporation
 *  			andrzej@chaeron.com
 */

package com.chaeron.GPS.Garmin;

//***************************************************************************
//*
//* 	Imports
//*
//***************************************************************************/

import com.chaeron.GPS.*;
import com.chaeron.GPS.event.*;
import com.chaeron.GPS.event.decorator.*;
import com.chaeron.GPS.exception.*;

import com.chaeron.platform.common.*;


//***************************************************************************
//*
//* 	RouteTransferHandler Class definition
//*
//***************************************************************************/

/**
 * The RouteTransferHandler class implements Garmin-specific Route transfer functions
 *
 */
	
class RouteTransferHandler extends TransferHandler implements GPSListener
{
	
	//***************************************************************************
	//*
	//* 	Attributes
	//*
	//***************************************************************************/
	
	private GarminGPSImpl				gps;

	private GarminRoute					currentRoute	= null;
	private Vector						routes			= null;
	private Vector						waypoints		= null;
	private Vector						links			= null;
	
	
	//***************************************************************************
	//*
	//* 	Constructors
	//*
	//***************************************************************************/
	
	/**
	 * Create a new RouteTransferHandler
	 *
	 */
	
	public RouteTransferHandler( GarminGPSImpl gps )
	{
		super();
		
		this.gps = gps;
	}

	
	//***************************************************************************
	//*
	//* 	Waypoint Handling Methods
	//*
	//***************************************************************************/
	
	/**
	 * Download Routes from the GPS unit.  
	 *
	 * @throws GPSException
	 */
	
	protected void downloadRoutes() throws GPSException
	{
		String			poll	= ".";
		
		if( !transferInProgress ) {
			// Listen for the Waypoint transfer events
			
			routes 			= null;
			waypoints 		= null;
			links 			= null;
			currentRoute	= null;
			
			gps.addGPSListener( this, GPSEvent.GPS_EVENT_RECORDS 
									| GPSEvent.GPS_EVENT_ROUTE_HEADER 
									| GPSEvent.GPS_EVENT_WAYPOINT 
									| GPSEvent.GPS_EVENT_ROUTE_LINK_DATA 
									| GPSEvent.GPS_EVENT_XFER_COMPLETE );
			startDownloadRoutes();
		} else {
			throw( new GPSOperationUnavailableException() );
		}

	}
	
	
	/**
	 * Start Downloading Waypoints from the GPS unit. 
	 *
	 * @throws GPSException
	 */
	 
	protected boolean startDownloadRoutes() throws GPSException
	{
		boolean					ret		= false;
		CommandGarminPacket		command;
		
		if( !transferInProgress && gps.getProtocol().isProtocolSet() ) {
			command = new CommandGarminPacket( gps.getProtocol() );
			
			if( command.setCommandType( Protocol.COMMAND_XFER_ROUTES, gps.getProtocol() ) ) {
				transferInProgress 			= true;
				pendingException			= null;
				transferRecordsRemaining   	= 0;
				transferType 				= GPSEvent.GPS_EVENT_ROUTE_HEADER;
				gps.sendPacket( command );
				ret = true;
			} else {
				throw( new GPSFunctionUnavailableException() );
			}
		} else {
			throw( new GPSOperationUnavailableException() );
		}
		
		return( ret );
	}
	
	
	/**
	 * Done Downloading Routes from the GPS unit. 
	 */
	
	protected void doneDownloadRoutes()
	{
		GPSEvent		event = new GPSEvent( GPSEvent.GPS_EVENT_ROUTE_TRANSFER_COMPLETE, null );
		
		gps.removeGPSListener( this );
		
		if( pendingException != null ) {
			event.setValue( pendingException );
			event.setException( pendingException );
		} else if( routes == null || currentRoute == null ) {
			event.setException( new GPSTransferException( GPSTransferException.EXCEPTION_INVALID_NUMBER_XFER_RECORDS ) );
			event.setValue( event.getException() );
		} else {
			currentRoute.setWaypoints( waypoints );
			currentRoute.setLinks( links );
			if( Compile.INCLUDE_EVENT_DECORATORS ) {
				GPSRouteDecorator	decorator = new GPSRouteDecorator( currentRoute );
				decorator.addMethodsImplemented( currentRoute.getMethodsImplemented() );
				routes.add( decorator );
			} else {
				routes.add( currentRoute );
			}
			event.setValue( routes );
		}
		
		routes 			= null;
		waypoints 		= null;
		links 			= null;
		currentRoute	= null;
		reset();
		
		gps.postGPSEvent( event );
	}
	
	
	//***************************************************************************
	//*
	//* 	Route Callback Methods
	//*
	//***************************************************************************/
	
	/**
	 * Used internally to process Route events
	 *
	 */
	
	public boolean onGPSEvent( GPSEvent event )  
	{	
		boolean			handled 	= false;
		Object			eventValue	= event.getValue();
		
		switch( event.getType() ) {
				
			case GPSEvent.GPS_EVENT_RECORDS:
				GPSRecords records;
				
				if( routes == null ) {
					records 					= (GPSRecords)eventValue;
					transferRecordsRemaining 	= records.getRecords();
					routes 						= new Vector();
					waypoints 					= new Vector();
				} else {
					pendingException = new GPSTransferException( GPSTransferException.EXCEPTION_INVALID_NUMBER_XFER_RECORDS );
					doneDownloadRoutes();
				}
				
				handled = true;
				break;
				
			case GPSEvent.GPS_EVENT_ROUTE_HEADER:
				RouteHeaderGarminPacket		routeHeader;
				
				if( routes != null ) { 					// Ignore if unfinished transfer
					transferRecordsRemaining--;
					if( currentRoute != null ) {
						currentRoute.setWaypoints( waypoints );
						currentRoute.setLinks( links );
						if( Compile.INCLUDE_EVENT_DECORATORS ) {
							GPSRouteDecorator	decorator = new GPSRouteDecorator( currentRoute );
							decorator.addMethodsImplemented( currentRoute.getMethodsImplemented() );
							routes.add( decorator );
						} else {
							routes.add( currentRoute );
						}
					}
					
					currentRoute = new GarminRoute();
					currentRoute.setHeader( (RouteHeaderGarminPacket)eventValue );
					waypoints 	 = new Vector();
					links 		 = null;
				}
				break;

			case GPSEvent.GPS_EVENT_WAYPOINT:
				if( routes != null ) { 					// Ignore  if unfinished transfer
					waypoints.add( eventValue );
					transferRecordsRemaining--;
				} 
				break;
				
			case GPSEvent.GPS_EVENT_ROUTE_LINK_DATA:
				RouteLinkDataGarminPacket		linkData;
				
				if( routes != null ) { 					// Ignore if unfinished transfer
					transferRecordsRemaining--;
					
					if( links == null ) {
						links = new Vector();
					}
					
					links.add( eventValue );
				}
				break;
				
			case GPSEvent.GPS_EVENT_XFER_COMPLETE:
				GPSXferComplete xferComplete;
				
				if( routes != null ) {							
					xferComplete = (GPSXferComplete)eventValue;
					
					if( xferComplete.getXferType() != transferType ) {
						routes = null;
						pendingException = new GPSTransferException( GPSTransferException.EXCEPTION_INVALID_XFER_COMPLETE );
					} else if( transferRecordsRemaining != 0 ) {
						routes = null;
						pendingException = new GPSTransferException( GPSTransferException.EXCEPTION_INVALID_NUMBER_XFER_RECORDS );
					}
				} else {
					pendingException = new GPSTransferException( GPSTransferException.EXCEPTION_INVALID_XFER_COMPLETE );
				}
				
				doneDownloadRoutes();
				
				handled = true;
				break;
				
		}
		
		
		return( handled );
	}
	
}

